/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Modele;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import javax.swing.Timer;

/**
 *
 * @author p0902486
 */
public class Grille extends Observable {

    public Grille() {
        super();
        build();
    }
    public int[][] grille;
    public boolean vide;
    public Forme pieceCourante;
    public Forme pieceSuivante;
    int delay = 510;
    boolean gameOver;
    boolean pause;
    Timer temps;

    public void build() {
        grille = new int[10][20];
        int i, j;
        for (i = 0; i < 20; ++i) {
            for (j = 0; j < 10; ++j) {
                grille[j][i] = 0;
            }
        }
        this.vide = true;
        this.pause = false;
        pieceCourante = new Forme();
        pieceSuivante = new Forme();
        genererPiece();
        gameOver = false;
        temps = new Timer(delay, taskPerformer);
    }

    public void ajoutObservateur(Observer obs) {
        this.addObserver(obs);
    }

    public void demarrerTimer() {
        temps.start();
    }

    public void nouvellePartie() {
        pause = false;
        gameOver = false;
        effacerGrille(0);
        nouvellePiece();
        temps.start();
    }

    public void effacerGrille(int num) {
        int i, j;
        for (i = 0; i < 20; ++i) {
            for (j = 0; j < 10; ++j) {
                grille[j][i] = num;
                notifierCase(j, i, num);
            }
        }
    }

    public void pause() {
        if (this.pause) {
            this.temps.start();
            this.pause = false;
        } else {
            this.temps.stop();
            this.pause = true;
        }
    }

    public void genererPiece() {
        Random rand = new Random();
        Random rand2 = new Random();
        int numero = rand.nextInt(7) + 1;
        int orientation = rand2.nextInt(4) + 1;
        this.pieceSuivante.setForme(numero, orientation);
        this.pieceSuivante.setCoord(3, -4);
        this.afficherApercu();
    }

    public void nouvellePiece() {
        pieceSuivante.copierForme(pieceSuivante, pieceCourante);
        this.pieceCourante.setCoord(3, 0);
        this.genererPiece();
        this.afficherPiece();
    }

    public void insererForme() {
        int i, j;
        int[] matrice;
        matrice = this.pieceCourante.getMatrice();

        for (i = 0; i < 4; ++i) {
            for (j = 0; j < 4; ++j) {
                if (matrice[j + 4 * i] > 0) {
                    this.grille[pieceCourante.coords[0] + j][pieceCourante.coords[1] + i] = matrice[j + 4 * i];
                    this.notifierCase(this.pieceCourante.coords[0] + j, this.pieceCourante.coords[1] + i,
                            this.grille[this.pieceCourante.coords[0] + j][this.pieceCourante.coords[1] + i]);
                }
            }
        }
        testerLignes(19);
        nouvellePiece();
    }

    public void effacerCase(int x, int y) {
        this.notifierCase(x, y, 0);
    }

    public void notifierCase(int x, int y, int source) {
        int[] coord = new int[3];
        coord[0] = x;
        coord[1] = y;
        if (coord[0] >= 0 && coord[0] < 10 && coord[1] >= 0 && coord[1] < 20) {
            coord[2] = source;
            setChanged();
            notifyObservers(coord);
        }
    }

    public void notifierPiece(int x, int y) {
        int i, j;
        for (i = 0; i < 4; ++i) {
            for (j = 0; j < 4; ++j) {
                if (this.pieceCourante.getValeur(j, i) == this.pieceCourante.getNumero()) {
                    notifierCase(j + x, i + y, this.pieceCourante.getValeur(j, i));
                }
            }
        }
    }

    public void afficherPiece() {
        if (!collision(0, 0)) {
            int coordX, coordY;
            coordX = this.pieceCourante.getCoord()[0];
            coordY = this.pieceCourante.getCoord()[1];
            notifierPiece(coordX, coordY);
        } else {
            this.temps.stop();
            this.gameOver = true;
            System.out.println("Stop");
        }

    }

    public void afficherApercu() {
        int coordX, coordY;
        int[] coord = new int[3];
        coordX = this.pieceSuivante.getCoord()[0];
        coordY = this.pieceSuivante.getCoord()[1];
        int i, j;
        for (i = 0; i < 4; ++i) {
            coord[1] = i + coordY;
            for (j = 0; j < 4; ++j) {
                coord[0] = j + coordX;
                coord[2] = this.pieceSuivante.getValeur(j, i);
                setChanged();
                notifyObservers(coord);
            }
        }
    }

    public boolean horsChamp(int x, int y) {
        int i, j, coordX, coordY;
        coordX = this.pieceCourante.coords[0] + x;
        coordY = this.pieceCourante.coords[1] + y;
        for (i = coordY; i < coordY + 4; ++i) {
            for (j = coordX; j < coordX + 4; ++j) {
                if (this.pieceCourante.getValeur(j - coordX, i - coordY) > 0
                        && (j < 0 || j >= 10 || i < 0 || i >= 20)) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean horsChampRotation() {
        int i, j, coordX, coordY;
        int sens = this.pieceCourante.getOrientation();
        this.pieceCourante.augmenteOrientation();
        coordX = this.pieceCourante.coords[0];
        coordY = this.pieceCourante.coords[1];
        for (i = coordY; i < coordY + 4; ++i) {
            for (j = coordX; j < coordX + 4; ++j) {
                if (this.pieceCourante.getValeur(j - coordX, i - coordY) > 1
                        && (j < 0 || j >= 10 || i < 0 || i >= 20)) {
                    this.pieceCourante.setOrientation(sens);
                    return true;
                }
            }
        }
        this.pieceCourante.setOrientation(sens);
        return false;
    }

    public boolean collision(int x, int y) {
        int i, j;
        if (!horsChamp(x, y)) {
            for (i = 0; i < 4; ++i) {
                for (j = 0; j < 4; ++j) {
                    if (this.pieceCourante.getValeur(j, i) > 0) {
                        if (this.pieceCourante.getValeur(j, i)
                                + this.grille[this.pieceCourante.getCoord()[0] + x + j][this.pieceCourante.getCoord()[1] + y + i]
                                > this.pieceCourante.getNumero()) {
                            if (y > 0) {
                                insererForme();
                            }
                            return true;
                        }
                    }
                }
            }

        }
        return false;
    }

    public boolean collisionRotation() {
        int i, j, orientation;
        orientation = this.pieceCourante.getOrientation();
        Forme piece = new Forme();
        piece.setNumero(this.pieceCourante.getNumero());
        piece.setOrientation(orientation);
        piece.augmenteOrientation();
        piece.setCoord(this.pieceCourante.getCoord()[0], this.pieceCourante.getCoord()[1]);
        if (!horsChampRotation()) {
            for (i = 0; i < 4; ++i) {
                for (j = 0; j < 4; ++j) {
                    if (piece.getValeur(j, i) > 0) {
                        if (piece.getValeur(j, i)
                                + this.grille[piece.getCoord()[0] + j][piece.getCoord()[1] + i]
                                > piece.getNumero()) {
                            return true;
                        }
                    }
                }
            }

        }
        return false;
    }

    public boolean bloque(int x, int y) {
        if (!horsChamp(x, y) && !gameOver && !pause) {
            return collision(x, y);
        } else {
            return true;
        }
    }

    public boolean basGrille() {
        int i, j, coordX, coordY;
        coordX = this.pieceCourante.coords[0];
        coordY = this.pieceCourante.coords[1];
        for (i = coordY; i < coordY + 4; ++i) {
            for (j = coordX; j < coordX + 4; ++j) {
                if (this.pieceCourante.getValeur(j - coordX, i - coordY) > 0
                        && (i >= 19)) {
                    insererForme();
                    return true;
                }
            }
        }
        return false;
    }

    public void effacerForme() {
        int i, j;
        for (i = 0; i < 4; ++i) {
            for (j = 0; j < 4; ++j) {
                if (this.pieceCourante.getValeur(j, i) == this.pieceCourante.numero) {
                    this.effacerCase(this.pieceCourante.coords[0] + j, this.pieceCourante.coords[1] + i);
                }
            }
        }
    }

    public void detruireLigne(int ligne) {
        int i, j;
        for (i = ligne; i >= 0; --i) {
            for (j = 0; j < 10; ++j) {
                this.grille[j][i] = 0;
                effacerCase(j, i);
                if (i > 0) {
                    this.grille[j][i] = this.grille[j][i - 1];
                    notifierCase(j, i, this.grille[j][i - 1]);
                }
            }
        }
    }

    public boolean ligneComplete(int ligne) {
        int i, somme;
        somme = 0;
        for (i = 0; i < 10; ++i) {
            if (this.grille[i][ligne] > 0) {
                ++somme;
            } else {
                return false;
            }
        }
        return somme == 10;
    }

    public void testerLignes(int ligne) {
        int i;
        for (i = ligne; i >= 0; --i) {
            if (ligneComplete(i)) {
                detruireLigne(i);
                testerLignes(i);
            }
        }

    }

    public void descendre() {
        if (!bloque(0, 1) && !basGrille()) {
            this.effacerForme();
            this.pieceCourante.coords[1]++;
            afficherPiece();
        }
    }

    public void tomber() {
        while (!bloque(0, 1) && !basGrille()) {
            descendre();
        }
    }

    public void MvtDroite() {
        if (!bloque(1, 0)) {
            this.effacerForme();
            this.pieceCourante.coords[0]++;
            afficherPiece();
        }
    }

    public void MvtGauche() {
        if (!bloque(-1, 0)) {
            this.effacerForme();
            this.pieceCourante.coords[0]--;
            this.afficherPiece();
        }
    }

    public void rotation() {
        if (!horsChampRotation() && !collisionRotation() && !pause) {
            if (this.pieceCourante.numero != 0) {
                this.effacerForme();
                this.pieceCourante.augmenteOrientation();
                this.afficherPiece();
            }
        }
    }

    ActionListener taskPerformer = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            if (basGrille() || bloque(0, 1)) {
            } else {
                descendre();
            }

        }
    };

    public void ecrireGrille() {
        int i, j;
        for (i = 0; i < 20; ++i) {
            for (j = 0; j < 10; ++j) {
                System.out.print(this.grille[j][i]);
            }
            System.out.print("\n");
        }
    }

}
